﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;

namespace LeadChina.AssemblyLine.Common
{
    /// <summary>
    /// 
    /// </summary>
    public class CIPNJCompolet
    {
        /// <summary>
        /// 判断CIP是否连接成功
        /// </summary>
        public event Action<string> ActiveEventHandler;


        /// <summary>
        /// 
        /// </summary>
        public OMRON.Compolet.CIP.NJCompolet njCompolet1;
        // 定义一个静态变量来保存类的实例
        private static CIPNJCompolet uniqueInstance;
        // 定义一个标识确保线程同步
        private static readonly object locker = new object();

        /// <summary>
        /// 内容相同返回true,反之返回false。
        /// </summary>
        /// <param name="arr1"></param>
        /// <param name="arr2"></param>
        /// <returns></returns>
        public static bool CompareArr(object[] arr1, object[] arr2)
        {
            var q = from a in arr1 join b in arr2 on a equals b select a;
            bool flag = arr1.Length == arr2.Length && q.Count() == arr1.Length;
            return flag;
        }

        #region 属性
        private Dictionary<string, object> _prop = new Dictionary<string, object>();
        /// <summary>
        /// 
        /// </summary>
        public Dictionary<string, object> Prop
        {

            get { return _prop; }
            set
            {
                Dictionary<string, int[]> data = new Dictionary<string, int[]>();
                foreach (KeyValuePair<string, object> item in value)
                {
                    if (!Prop.ContainsKey(item.Key))
                    {
                        _prop.Add(item.Key, item.Value);
                        continue;
                    }
                    //是否有变化
                    if ((item.Value == null && Prop[item.Key] != null) || !item.Value.Equals(Prop[item.Key]))
                    {
                        Type type = item.Value.GetType();
                        //数组
                        if (type.IsArray)
                        {
                            object[] newValue = new object[20];
                            object[] oldValue = new object[20]; 
                            if (item.Value is Int64[])
                            {
                                newValue = new object[((Int64[])item.Value).Length];
                                for (int i = 0; i < newValue.Length; i++)
                                {
                                    newValue[i] = ((Int64[])item.Value)[i];
                                }
                                oldValue = new object[((Int64[])Prop[item.Key]).Length];
                                for (int i = 0; i < oldValue.Length; i++)
                                {
                                    oldValue[i] = ((Int64[])Prop[item.Key])[i];
                                }
                            }
                            else if (item.Value is byte[])
                            {
                                newValue = new object[((byte[])item.Value).Length];
                                oldValue = new object[((byte[])Prop[item.Key]).Length];

                                for (int i = 0; i < newValue.Length; i++)
                                {
                                    newValue[i] = ((byte[])item.Value)[i];

                                }
                                for (int i = 0; i < oldValue.Length; i++)
                                {
                                    oldValue[i] = ((byte[])Prop[item.Key])[i];
                                }
                            }



                            List<int> dif = new List<int>();
                            for (int i = 0; i < newValue.Length; ++i)
                            {
                                if ((newValue[i] == null && oldValue[i] != null) || !newValue[i].Equals(oldValue[i]))
                                {
                                    dif.Add(i);
                                }
                            }
                            if (dif.Count > 0) data.Add(item.Key, dif.ToArray());
                        }
                        else
                        {
                            data.Add(item.Key, new int[] { 0 });
                        }
                        _prop[item.Key] = item.Value;
                    }
                }
                if (DataAlter != null && data.Count > 0) DataAlter(data, value);
            }
        }
        #endregion
        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataIndex"></param>
        /// <param name="dataDic"></param>
        public delegate void DelegateDataAlter(Dictionary<string, int[]> dataIndex, Dictionary<string, object> dataDic);
        /// <summary>
        /// 
        /// </summary>
        public DelegateDataAlter DataAlter;
        /// <summary>
        /// 构造函数
        /// </summary>
        public static CIPNJCompolet GetInstance()
        {
            // 当第一个线程运行到这里时，此时会对locker对象 "加锁"，
            // 当第二个线程运行该方法时，首先检测到locker对象为"加锁"状态，该线程就会挂起等待第一个线程解锁
            // lock语句运行完之后（即线程运行完之后）会对该对象"解锁"
            // 双重锁定只需要一句判断就可以了
            if (uniqueInstance == null)
            {
                lock (locker)
                {
                    // 如果类的实例不存在则创建，否则直接返回
                    if (uniqueInstance == null)
                    {
                        uniqueInstance = new CIPNJCompolet();
                    }
                }
            }
            return uniqueInstance;
        }

        /// <summary>
        /// 激活CIP
        /// </summary>
        /// <param name="varlist"></param>
        /// <param name="ipAddress">IP地址</param>
        /// <param name="interval"></param>
        public void Active(string[] varlist, string ipAddress = "192.168.250.1", int interval = 200)
        {
            try
            {
                njCompolet1 = new OMRON.Compolet.CIP.NJCompolet();
                njCompolet1.PeerAddress = ipAddress;
                njCompolet1.Active = true;
                if (!njCompolet1.IsConnected)
                {
                    njCompolet1.Active = false;
                    ActiveEventHandler?.Invoke("初始化CIPNJ失败！");
                }
                else
                {
                    ActiveEventHandler?.Invoke("初始化CIPNJ完毕！");
                    Task.Factory.StartNew(() => OnDataAlter(varlist, interval));
                }
            }
            catch (Exception ex)
            {
                njCompolet1.Active = false;
                ActiveEventHandler?.Invoke("初始化CIPNJ失败，异常：" + ex.Message);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="varlist"></param>
        /// <param name="interval"></param>
        protected virtual void OnDataAlter(string[] varlist, int interval)
        {
            while (njCompolet1.Active)
            {
                try
                {
                    Dictionary<string, object> dic = new Dictionary<string, object>();
                    Hashtable retVals = null;
                    lock (locker)
                    {
                        retVals = this.njCompolet1.ReadVariableMultiple(varlist);
                    }
                    if (retVals == null)
                    {
                        throw new NotSupportedException();
                    }
                    string multival = string.Empty;
                    for (int index = 0; index < varlist.Length; index++)
                    {
                        string varName = varlist[index];
                        object val = retVals[varName];
                        dic.Add(varName, val);
                    }
                    

                    this.Prop = dic;
                }
                catch (Exception ex)
                {
                    ActiveEventHandler?.Invoke("监听CIPNJ数据失败，异常：" + ex.Message);
                }
                if (interval != 0)
                {
                    Thread.Sleep(interval);
                }
            }
        }

        /// <summary>
        /// 关闭CIP
        /// </summary>
        public void DisActive()
        {
            try
            {
                njCompolet1.Active = false;
                ActiveEventHandler?.Invoke("关闭CIPNJ连接完毕！");
            }
            catch (Exception ex)
            {
                ActiveEventHandler?.Invoke("关闭CIPNJ连接失败，异常：" + ex.Message);
            }
        }
        /// <summary>
        /// 读取数据（传入泛型）
        /// </summary>
        /// <typeparam name="T">传入的类型</typeparam>
        /// <param name="ReadVariable">欧姆龙变量名</param>
        /// <returns></returns>
        public T Read<T>(string ReadVariable)
        {
            try
            {
                object obj = null;
                lock (locker)
                {
                    obj = this.njCompolet1.ReadVariable(ReadVariable);
                }
                if (obj == null)
                {
                    throw new NotSupportedException();
                }
                return (T)obj;
            }
            catch (Exception ex)
            {
                //throw ex;
                return default(T);
            }
        }
        /// <summary>
        /// 读取多个欧姆龙变量
        /// </summary>
        /// <param name="varlist">读取的变量数组</param>
        /// <returns>返回字典，key为变量名，value为变量值</returns>
        public Dictionary<string, object> Read(string[] varlist)
        {
            Dictionary<string, object> dic = new Dictionary<string, object>();
            try
            {
                Hashtable retVals = null;
                lock (locker)
                {
                    retVals = this.njCompolet1.ReadVariableMultiple(varlist);
                }

                if (retVals == null)
                {
                    throw new NotSupportedException();
                }
                string multival = string.Empty;
                for (int index = 0; index < varlist.Length; index++)
                {
                    string varName = varlist[index];
                    object val = retVals[varName];
                    dic.Add(varName, val);
                }
                return dic;
            }
            catch 
            {
                return dic;
            }
        }
        /// <summary>
        /// 写入PLC值
        /// </summary>
        /// <param name="variableName">变量名</param>
        /// <param name="writeData">变量值</param>
        public void Write(string variableName, object writeData)
        {
            lock (locker)
            {
                njCompolet1.WriteVariable(variableName, writeData);

            }
        }

        private byte[] ObjectToByteArray(string[] obj)
        {
            if (obj is Array)
            {
                Array arr = obj as Array;
                Byte[] bin = new Byte[arr.Length];
                for (int i = 0; i < bin.Length; i++)
                {
                    bin[i] = Convert.ToByte(arr.GetValue(i));
                }
                return bin;
            }
            else
            {
                return new Byte[1] { Convert.ToByte(obj) };
            }
        }

        private object RemoveBrackets(string val)
        {
            object obj = string.Empty;
            if (val.IndexOf("[") >= 0)
            {
                string str = val.Trim('[', ']');
                str = str.Replace("][", ",");
                obj = str.Split(',');
            }
            else
            {
                obj = val;
            }
            return obj;
        }

        private string GetValueString(object val)
        {
            if (val is float || val is double)
            {
                return string.Format("{0:R}", val);
            }
            else
            {
                return val.ToString();
            }
        }

        private List<string> ReadStruct(string varname, List<string> readWay)
        {
            object obj = null;
            obj = this.njCompolet1.ReadVariable(varname);
            if (obj == null)
            {
                throw new NotSupportedException();
            }
            List<string> str_list = new List<string>();
            //VariableInfo info = this.njCompolet1.GetVariableInfo(varname);
            byte[] b = (byte[])(obj);
            var i = string.Empty;
            var sequence = 0;
            for (int j = 0; j < readWay.Count; j++)
            {
                string temp_str = string.Empty;
                switch (readWay[j])
                {
                    case "int":
                        for (int k = sequence + 3; k >= sequence; k--)
                        {
                            temp_str += b[k].ToString("x2");
                        }
                        str_list.Add(ByteToInt(temp_str).ToString());
                        break;
                    case "float":
                        for (int k = sequence; k <= sequence + 3; k++)
                        {
                            temp_str += b[k].ToString("x2");
                        }
                        str_list.Add(ByteToFloat(temp_str).ToString());
                        break;
                    case "bool":
                        for (int k = sequence + 3; k >= sequence; k--)
                        {
                            temp_str += b[k].ToString("x2");
                        }
                        str_list.Add(GetBitValue(Convert.ToInt32((temp_str), 16), 0).ToString());
                        break;
                }
                sequence = sequence + 4;
            }
            return str_list;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="varname"></param>
        /// <param name="readWay"></param>
        /// <returns></returns>
        public string[] WriteStruct(string[] varname, List<string> readWay)
        {
            string[] str_res = new string[varname.Length * 4];
            string tempStr = string.Empty;
            for (int j = 0; j < readWay.Count; j++)
            {
                string temp_str = string.Empty;
                switch (readWay[j])
                {
                    case "int":
                        tempStr = IntToString(Convert.ToInt32(varname[j]));
                        str_res[j * 4] = Convert.ToInt32(tempStr.Substring(0, 2)).ToString();
                        str_res[j * 4 + 1] = Convert.ToInt32(tempStr.Substring(2, 2)).ToString();
                        str_res[j * 4 + 2] = Convert.ToInt32(tempStr.Substring(4, 2)).ToString();
                        str_res[j * 4 + 3] = Convert.ToInt32(tempStr.Substring(6, 2)).ToString();
                        break;
                    case "float":
                        byte[] byte4 = new byte[4];
                        byte4 = FloatToString(varname[j]);
                        str_res[j * 4] = byte4[0].ToString();
                        str_res[j * 4 + 1] = byte4[1].ToString();
                        str_res[j * 4 + 2] = byte4[2].ToString();
                        str_res[j * 4 + 3] = byte4[3].ToString();
                        break;
                    case "bool":
                        if (varname[j] == "True")
                        {
                            str_res[j * 4] = "1";
                            str_res[j * 4 + 1] = "0";
                            str_res[j * 4 + 2] = "0";
                            str_res[j * 4 + 3] = "0";
                        }
                        else if (varname[j] == "False")
                        {
                            str_res[j * 4] = "0";
                            str_res[j * 4 + 1] = "0";
                            str_res[j * 4 + 2] = "0";
                            str_res[j * 4 + 3] = "0";
                        }
                        else
                        {
                            str_res[j * 4] = "0";
                            str_res[j * 4 + 1] = "0";
                            str_res[j * 4 + 2] = "0";
                            str_res[j * 4 + 3] = "0";
                        }
                        break;
                }
            }
            return str_res;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public string IntToString(int value)
        {
            string bytes = value.ToString("x2");
            if (bytes.Length != 8)
            {
                bytes = bytes.PadLeft(8, '0');
            }
            //定义一个单字符数组
            char[] newStr = new char[bytes.Length];
            newStr[0] = bytes[6];
            newStr[1] = bytes[7];
            newStr[2] = bytes[4];
            newStr[3] = bytes[5];
            newStr[4] = bytes[2];
            newStr[5] = bytes[3];
            newStr[6] = bytes[0];
            newStr[7] = bytes[1];
            string str = string.Empty;
            for (int j = 0; j < 8; j++)
            {
                str += newStr[j].ToString();
            }
            return str;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public byte[] FloatToString(string value)
        {
            byte[] byteArray = new byte[4] { 0, 0, 0, 0 };
            byteArray = System.Text.Encoding.Default.GetBytes(value);
            return byteArray;
        }
        private float ByteToFloat(string s)
        {
            MatchCollection matches = Regex.Matches(s, @"[0-9A-Fa-f]{2}");
            byte[] bytes = new byte[matches.Count];
            for (int j = 0; j < bytes.Length; j++)
            {
                bytes[j] = byte.Parse(matches[j].Value, NumberStyles.AllowHexSpecifier);
            }

            return BitConverter.ToSingle(bytes, 0);
        }

        private int ByteToInt(string s)
        {
            return Convert.ToInt32(s, 16);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public bool GetBitValue(int value, int index)
        {
            if (index > 15) throw new ArgumentOutOfRangeException("index"); //索引出错
            var val = 1;
            if (index != 0)
            { val = 1 << index; }
            return (value & val) == val;
        }
    }
}
